#ifndef __M__BINDING_H_
#define __M__BINDING_H_

#include "../mqCommon/Helper.hpp"
#include "../mqCommon/Logger.hpp"
#include "../mqCommon/proto/mq_message.pb.h"
#include <iostream>
#include <unordered_map>
#include <memory>
#include <mutex>

using namespace mq_message;
using namespace mq;
using namespace std;

namespace mq
{
    struct Binding;
    using b_ptr = shared_ptr<Binding>;

    using MsgQueueBindingMap = unordered_map<string /*队列*/, b_ptr /*绑定数据*/>;
    using BindingMap = unordered_map<string /*交换机*/, MsgQueueBindingMap /*该交换机绑定的所有队列*/>;
    // 在一台交换机绑定的所有队列中，每个队列与该交换机的绑定数据是唯一的

    // 绑定数据类
    struct Binding
    {
        using ptr = shared_ptr<Binding>;
        string _ename;       // 交换机名称
        string _qname;       // 队列名称
        string _binding_key; // 绑定信息
        Binding(const string &ename, const string &qname, const string &binding_key)
            : _ename(ename), _qname(qname), _binding_key(binding_key) {}
        Binding() {}
    };

    // 绑定数据持久化管理类
    class BindingMapper
    {
    public:
        BindingMapper(const string &dbfile) : _sql_helper(dbfile)
        {
            // 创建文件前先创建目标路径
            string path = FileHelper::parentDirectory(dbfile);
            bool ret1 = FileHelper::createDirectory(path);
            if (!ret1)
            {
                ELOG("创建数据库文件路径时出错：%s", path);
                abort();
            }

            bool ret2 = _sql_helper.open(); // 打开/创建数据库文件
            if (!ret2)
            {
                ELOG("打开数据库文件失败：%s", dbfile);
                abort();
            }
            createTable();
        }
        void createTable() // 创建绑定数据表
        {
#define CREATE_TABLE "create table if not exists binding_table(\
                            exchange_name varchar(32), msgqueue_name varchar(32), binding_key varchar(128));"
            bool ret = _sql_helper.exec(CREATE_TABLE, NULL, NULL);
            if (!ret)
            {
                ELOG("创建绑定数据库表失败");
                abort();
            }
        }
        void removeTable() // 删除绑定数据表
        {
#define DROP_TABLE "drop table if exists binding_table;"
            bool ret = _sql_helper.exec(DROP_TABLE, NULL, NULL);
            if (!ret)
            {
                ELOG("删除绑定数据库表失败");
                abort();
            }
        }
        bool insert(b_ptr &binding) // 新增绑定
        {
#define INSERT_SQL "insert into binding_table values('%s','%s','%s');"
            char sql_str[256];
            snprintf(sql_str, sizeof(sql_str) - 1, INSERT_SQL, binding->_ename.c_str(), binding->_qname.c_str(), binding->_binding_key.c_str());
            bool ret = _sql_helper.exec(sql_str, NULL, NULL);
            if (!ret)
            {
                ELOG("新增绑定数据失败：%s & %s & %s", binding->_ename.c_str(), binding->_qname.c_str(), binding->_binding_key.c_str());
                return false;
            }
            return true;
        }
        bool remove(const string &ename, const string &qname) // 删除指定绑定
        {
            std::stringstream ss;
            ss << "delete from binding_table where exchange_name='" << ename << "' and msgqueue_name='" << qname << "';";
            bool ret = _sql_helper.exec(ss.str(), NULL, NULL);
            if (!ret)
            {
                ELOG("删除绑定数据失败：%s & %s", ename.c_str(), qname.c_str());
                return false;
            }
            return true;
        }
        bool removeExchangeBindings(const string &ename) // 删除指定交换机的所有绑定数据
        {
            std::stringstream ss;
            ss << "delete from binding_table where exchange_name='" << ename << "';";
            bool ret = _sql_helper.exec(ss.str(), NULL, NULL);
            if (!ret)
            {
                ELOG("删除指定交换机的绑定数据失败：%s & %s", ename.c_str());
                return false;
            }
            return true;
        }
        bool removeMsgQueueBindings(const string &qname) // 删除指定队列的所有绑定数据
        {
            std::stringstream ss;
            ss << "delete from binding_table where msgqueue_name='" << qname << "';";
            bool ret = _sql_helper.exec(ss.str(), NULL, NULL);
            if (!ret)
            {
                ELOG("删除指定队列的绑定数据失败：%s", qname.c_str());
                return false;
            }
            return true;
        }
        BindingMap recovery() // 获取所有绑定数据：恢复历史数据
        {
            string select_sql = "select exchange_name, msgqueue_name, binding_key from binding_table;"; // 查询所有交换机信息
            BindingMap bindingMap;
            bool ret = _sql_helper.exec(select_sql, selectCallback, (void *)&bindingMap);
            if (!ret)
            {
                ELOG("恢复绑定数据过程出错");
            }
            return bindingMap;
        }

    private:
        static int selectCallback(void *args, int col_num, char **row, char **fields) // 查询回调，每一行都调用该回调
        {
            // 1.获取结果保存容器
            BindingMap *result = (BindingMap *)args;
            // 2.读取一条绑定数据，将其进行恢复
            auto binding = make_shared<Binding>(row[0], row[1], row[2]); // 创建一个绑定数据对象

            // 3.插入该数据
            MsgQueueBindingMap &qmap = (*result)[binding->_ename]; // 若有，则返回引用；没有，则创建后返回引用
            qmap.insert(make_pair(binding->_qname, binding));      // 直接向该引用插入数据

            return 0;
        }

    private:
        SqliteHelper _sql_helper;
    };

    // 绑定数据内存管理类
    class BindingManager
    {
    public:
        using manager_ptr = shared_ptr<BindingManager>;
        BindingManager(const string &dbfile) : _mapper(dbfile)
        {
            _bindings = _mapper.recovery(); // 恢复历史数据
        }
        bool Bind(const string &ename, const string &qname, const string &binding_key, bool durable) // 绑定
        {
            unique_lock<mutex> lock(mtx);
            if (exists(ename, qname)) // 绑定已存在
                return true;
            b_ptr bind = make_shared<Binding>(ename, qname, binding_key); // 绑定对象
            MsgQueueBindingMap &msgq = _bindings[ename];
            msgq.insert(make_pair(qname, bind)); // 添加绑定
            if (durable)                         // 交换机和队列的持久化标志有效，该绑定才需要持久化
                return _mapper.insert(bind);
            return true;
        }
        void unBind(const string &ename, const string &qname) // 解绑定
        {
            unique_lock<mutex> lock(mtx);
            if (!exists(ename, qname)) // 绑定不存在
                return;
            MsgQueueBindingMap &msgq = _bindings[ename];
            msgq.erase(qname); // 删除绑定
            _mapper.remove(ename, qname);
        }
        void removeExchangeBindings(const string &ename) // 删除交换机所有绑定
        {
            unique_lock<mutex> lock(mtx);
            _bindings.erase(ename);
            _mapper.removeExchangeBindings(ename);
        }
        void removeMsgQueueBindings(const string &qname) // 删除队列所有绑定
        {
            unique_lock<mutex> lock(mtx);
            for (auto &it : _bindings)
            {
                it.second.erase(qname);
            }
            _mapper.removeMsgQueueBindings(qname);
        }
        MsgQueueBindingMap getExchangeBindings(const string &ename) // 获取一台交换机的所有绑定
        {
            unique_lock<mutex> lock(mtx);
            if (_bindings.find(ename) == _bindings.end())
            {
                DLOG("交换机不存在绑定：%s", ename.c_str());
                return MsgQueueBindingMap();
            }
            return _bindings[ename];
        }
        b_ptr getBinding(const string &ename, const string &qname) // 获取指定绑定
        {
            unique_lock<mutex> lock(mtx);
            if (!exists(ename, qname))
                return b_ptr();
            return _bindings[ename][qname];
        }
        size_t size() // 获取绑定数据的数量
        {
            unique_lock<mutex> lock(mtx);
            int cnt = 0;
            for (auto &it : _bindings)
            {
                cnt += it.second.size();
            }
            return cnt;
        }
        void clear() // 销毁所有绑定数据
        {
            unique_lock<mutex> lock(mtx);
            _mapper.removeTable();
            _bindings.clear();
        }

        bool exist_test(const string &ename, const string &qname) // 判断绑定是否存在
        {
            // 内部加锁，不能在其它加锁的成员函数内部被调用
            // 但可用于单元测试判断
            unique_lock<mutex> lock(mtx);
            auto it = _bindings.find(ename);
            if (it == _bindings.end())
                return false;
            MsgQueueBindingMap &qbmap = it->second;
            auto bind = qbmap.find(qname);
            if (bind == qbmap.end())
                return false;
            return true;
        }

    private:
        bool exists(const string &ename, const string &qname) // 判断绑定是否存在
        {                                                     // 内部未加锁，只能在其它加锁的成员函数内部被调用
            auto it = _bindings.find(ename);
            if (it == _bindings.end())
                return false;
            MsgQueueBindingMap &qbmap = it->second;
            auto bind = qbmap.find(qname);
            if (bind == qbmap.end())
                return false;
            return true;
        }

    private:
        mutex mtx;             // 加锁保护
        BindingMapper _mapper; // 持久化管理类
        BindingMap _bindings;  // map
    };

}

#endif